namespace System.IO.Ports
{
    using System.Text;
    
    public class SerialPortEx : SerialPort
    {
        private const int BUFFER_SIZE = 128;

		private const int DefaultBaudRate = 9600;
		private const int DefaultDataBits = 8;
		private const Parity DefaultParity = Parity.None;
		private const StopBits DefaultStopBits = StopBits.One;

        string newLine = "\r\n";

        public SerialPortEx (string portName) :
			this (portName, DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
		{
		}

		public SerialPortEx (string portName, int baudRate) :
			this (portName, baudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
		{
		}

		public SerialPortEx (string portName, int baudRate, Parity parity) :
			this (portName, baudRate, parity, DefaultDataBits, DefaultStopBits)
		{
		}

        public SerialPortEx(string portName, int baudRate, Parity parity, int dataBits) :
			this (portName, baudRate, parity, dataBits, DefaultStopBits)
		{
		}

        public SerialPortEx(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits) :
            base(portName, baudRate, parity, dataBits, stopBits)
        {
        }

        /// <summary>
        /// Gets or sets the value used to interpret the end of a call to the System.IO.Ports.SerialPort.ReadLine()
        /// and System.IO.Ports.SerialPort.WriteLine(System.String) methods.
        /// </summary>     
        public string NewLine
        {
            get
            {
                return newLine;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }

                if (value.Length == 0)
                {
                    throw new ArgumentException("NewLine cannot be null or empty.", "value");
                }

                newLine = value;
            }
        }

        public string ReadLine()
        {
            return ReadTo(newLine);
        }

        public string ReadTo(string value)
        {
            CheckOpen();
            if (value == null)
                throw new ArgumentNullException("value");
            if (value.Length == 0)
                throw new ArgumentException("value");

            // Turn into byte array, so we can compare
            byte[] byte_value = Encoding.UTF8.GetBytes(value);
            int current = 0;
            byte[] seen = new byte[BUFFER_SIZE];
            int seenCount = 0;

            while (true)
            {
                int n = ReadByte();
                if (n == -1)
                    break;
                seen[seenCount++] = (byte)n;
                if (n == byte_value[current])
                {
                    current++;
                    if (current == byte_value.Length)
                    {
                        return ZealC02.Encoding.UTF8.GetString(seen, 0, seen.Length - byte_value.Length);
                    }
                }
                else
                {
                    current = (byte_value[0] == n) ? 1 : 0;
                }
            }
            return ZealC02.Encoding.UTF8.GetString(seen);
        }

        public void Write(string str)
        {
            CheckOpen();
            if (str == null)
                throw new ArgumentNullException("str");

            byte[] buffer = Encoding.UTF8.GetBytes(str);
            Write(buffer, 0, buffer.Length);
        }

        public void WriteLine(string str)
        {
            this.Write(str + newLine);
        }

        private void CheckOpen()
        {
            if (!this.IsOpen)
            {
                throw new InvalidOperationException("Specified port is not open.");
            }
        }
    }
}
